/*
 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Lesser General Public License
 * (LGPL) version 2.1 which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/lgpl.html
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * Contributors:
 *     bstefanescu
 */
package org.nuxeo.gwt.habyt.upload.client.core;

import org.nuxeo.gwt.habyt.upload.client.FileRef;
import org.nuxeo.gwt.habyt.upload.client.FileWidgetProvider;
import org.nuxeo.gwt.habyt.upload.client.Uploader;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;

/**
 * A form used to upload in background a file. The form is containing an
 * {@link InputFileWrapper}.
 * 
 * When the submit starts the form is creating a new {@link FileRef} to
 * reference the file it is uploading and notify the uploader about the
 * submission by giving to it the file reference.
 * 
 * The uploader is keeping a queue of submitted files with their associated form
 * handlers so that it can cancel a submission at user request. If user
 * canceling occurs the form must be destroyed by calling the {@link #destroy()}
 * method.
 * 
 * Destroying a form means removing it from the DOM and freeing any resources
 * held by the form.
 * 
 * This form can perform only one upload. When the file is selected the upload
 * is queued and the form is moved outside the viewable area. The upload queue
 * will submit the form and when submit is done or canceled by the user the form
 * is destroyed.
 * 
 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
 * 
 */
public class UploadForm extends FormPanel implements ChangeHandler,
        SubmitHandler, SubmitCompleteHandler {

    protected Uploader uploader;

    protected InputFileWrapper input;

    protected FileRef ref;

    public UploadForm(Uploader uploader) {
        this(uploader, null);
    }

    /**
     * Create a new form given an uploader instance
     * 
     * @param uploader
     */
    public UploadForm(Uploader uploader, FileWidgetProvider provider) {
        this.uploader = uploader;
        if (provider != null) {
            input = InputFileWrapper.create();
            input.setVisibleWidget(provider.getFileWidget());
        } else {
            input = new InputFileWrapper();
        }
        input.getFileUpload().addChangeHandler(this);
        addSubmitCompleteHandler(this);
        addSubmitHandler(this);

        ref = new FileRef();

        setMethod(FormPanel.METHOD_POST);
        setEncoding(FormPanel.ENCODING_MULTIPART);
        setAction(uploader.getUploadUrl(ref));

        setWidget(input);
    }

    /**
     * Get the input file wrapper used by this form. Never returns null.
     * 
     * @return
     */
    public InputFileWrapper getInput() {
        return input;
    }

    /**
     * Get the reference of the file being submitted by this form. Never return
     * null.
     * 
     * @return
     */
    public FileRef getFileRef() {
        return ref;
    }

    /**
     * A file was selected in the File Browse dialog. This will add the form to
     * the upload queue and is disposing the input file wrapper. submission.
     */
    @Override
    public void onChange(ChangeEvent event) {
        ref.setName(input.getFileUpload().getFilename());
        uploader.addUploadRequest(this);
        hide();
    }

    @Override
    public void onSubmit(SubmitEvent event) {
        if (!uploader.onSubmit(this)) {
            event.cancel();
        }
    }

    /**
     * Submission was done. This will destroy the form and notify the uploader
     * about the submission status.
     * 
     * @param event
     */
    @Override
    public void onSubmitComplete(SubmitCompleteEvent event) {
        if (!readResponse(event.getResults().trim())) {
            // TODO handle errors
        }
        uploader.onSubmitDone(this);
        // System.out.println(">>>>>>SUBMITED");
        // destroy this form because it is no more needed
        destroy();
    }

    public boolean readResponse(String result) {
        Element el = Document.get().createDivElement();
        el.setInnerHTML(result);
        el = el.getFirstChildElement();
        if (el != null) {
            String id = el.getId();
            String title = el.getTitle();
            String text = el.getInnerText();
            if (ref.getId().equals(id)) {
                ref.setContentType(title);
                ref.setLength(text);
                return true;
            }
        }
        return false;
    }

    /**
     * Called when upload ended or canceled. Form should be destroyed (i.e.
     * removed from the DOM)
     */
    public void destroy() {
        input.dispose();
        DeferredCommand.addCommand(new Command() {
            @Override
            public void execute() {
                removeFromParent();
            }
        });
    }

    /**
     * Move the form away from visible area but don't destroy nor disable nor
     * hide nor the form.
     */
    public void hide() {
        input.dispose();
        getElement().getStyle().setPosition(Position.ABSOLUTE);
        getElement().getStyle().setLeft(-10000, Unit.PX);
    }

}
